Adapted from Childhood Cancer Data Lab, Alex’s Lemonade Stand Foundation training materials.
Background
Before we get into it, let’s briefly cover some concepts.
Dimension reduction (also called dimensionality reduction) is any technique that reduces the number of features or dimension in a dataset. A dimension in our high-dimensional space is the measurement of an individual gene in our RNA-seq data in our particular case. Dimension reduction can come about through feature elimination or feature selection, where we remove features or variables if they are unlikely to give us meaningful information (or are redundant with other variables). Or we can use feature extraction, where we form new features from the input data. (See Freytag (2019) and Raj (2019).)
In general, the idea behind dimension reduction, as we’ll cover it in this notebook, is that we want to transform our input data into a low-dimensional representation that retains some of the meaningful structure (feature extraction).
If we want to get the sense of the overall structure in our dataset, sometimes we want the following:

Representation learning (also called feature engineering or feature learning) is any technique that automatically detects representations from input data. We’ll cover unsupervised methods like matrix factorization here, but supervised methods like supervised neural networks are examples of representation learning, too.
When we’re working with gene expression data, often what we want is the following:

We may want to use these representations in a semi-supervised manner, where we learn representations from a collection of data without labels and then use them to encode a low-dimensional representation of data with labels that we then use for prediction.
Set up
# Set seed for reproducibility
set.seed(2020)
Libraries
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ─────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.3.2 ✓ purrr 0.3.4
✓ tibble 3.0.3 ✓ dplyr 1.0.0
✓ tidyr 1.1.0 ✓ stringr 1.4.0
✓ readr 1.3.1 ✓ forcats 0.5.0
── Conflicts ────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
# UMAP implementation
library(uwot)
Loading required package: Matrix
Attaching package: ‘Matrix’
The following objects are masked from ‘package:tidyr’:
expand, pack, unpack
# t-SNE implementation
library(Rtsne)
Files
clinical_file <- file.path("data", "pbta-medullo-histologies.tsv.gz")
# Processed RNA-seq data
rnaseq_file <- file.path("data", "pbta-medullo-vst-collapsed.tsv.gz")
# Pathway-Level Information ExtractoR model we've prepared ahead of time
plier_file <- file.path("models", "pbta-medullo-plier.RDS")
Read in data
Read in our transformed RNA-seq data.
rnaseq_df <- read_tsv(rnaseq_file)
Parsed with column specification:
cols(
.default = col_double(),
gene_symbol = col_character()
)
See spec(...) for full column specifications.
For the applications in this notebook, we’ll transpose RNA-seq data.
rnaseq_mat <- rnaseq_df %>%
column_to_rownames("gene_symbol") %>%
as.matrix() %>%
t()
Because we are interested in the overall structure of our data, we’re going to subset the matrix to features with high variance moving forward. Eliminating features that don’t vary within themselves and therefore are less likely to contribute to the overall structure in our data that we are most interested in. Fewer features is appealing from a computational resource standpoint!
# Genes are now columns
gene_variance <- matrixStats::colVars(rnaseq_mat)
# We'll want to retain the top 10%
variance_threshold <- quantile(gene_variance, 0.95)
# Filter to top 10%
high_var_mat <- rnaseq_mat[, gene_variance > variance_threshold]
Read in our clinical metadata file.
clinical_df <- read_tsv(clinical_file)
Parsed with column specification:
cols(
Kids_First_Biospecimen_ID = col_character(),
short_histology = col_character(),
broad_histology = col_character(),
tumor_descriptor = col_character(),
molecular_subtype = col_character()
)
For the most part, we’ll only use the molecular subtype information from the clinical meatadata.
subtype_df <- clinical_df %>%
select(Kids_First_Biospecimen_ID, molecular_subtype)
Dimension reduction
Adapted from CCDL scRNA-seq dimension reduction and Dimensionality Reduction concepts
Principal Components Analysis (PCA)
Principal components analysis (PCA) is a dimensionality reduction technique that captures the main sources of variation in our data in the first two principal components (PC1 and PC2). PCA applies linear algebra to find the axis of greatest variation in the data matrix, then the axis perpendicular to that with the most remaining variation, then the one perpendicular to those with the most remaining, etc.
# PCA
pca_results <- prcomp(high_var_mat, scale = TRUE)
# Join the first two PCs with the subtype labels for plotting purposes
pca_df <- data.frame(pca_results$x[, 1:2]) %>%
rownames_to_column("Kids_First_Biospecimen_ID") %>%
inner_join(subtype_df, by = "Kids_First_Biospecimen_ID")
# Create a plot of the first two PCs
ggplot(pca_df,
aes(x = PC1,
y = PC2,
color = molecular_subtype)) +
geom_point(alpha = 0.75) +
colorblindr::scale_color_OkabeIto() +
theme_bw()

PCA can sometimes produce results where the points occupy the whole plotting space, which would likely be more pronounced if we had even more samples.
Uniform Manifold Approximation and Projection (UMAP)
UMAP (Uniform Manifold Approximation and Projection) is a machine learning technique designed to provide more detail in highly dimensional data than a typical PCA. While PCA assumes that the variation we care about has a particular distribution (normal, broadly speaking), UMAP allows more complicated distributions that it learns from the data. We won’t go into the underlying mathematics, but you can look at the paper by McInnes, Healy, & Melville (2018). The main advantage of this change in underlying assumptions is that UMAP can do a better job separating clusters, especially when some of those clusters may be more similar to each other than others.
Another dimensionality reduction technique that you may have heard of is t-SNE (t-distributed Stochastic Neighbor Embedding), which has similar properties to UMAP, and often produces similar results. There is some ongoing debate about which of these two techniques is superior for high-dimensional biological data, and whether the differences are due to the underlying algorithm or to implementation and parameter initialization defaults. Regardless of why, in our experience, UMAP seems to produce slightly better results in this context and run a bit faster, but the differences can be subtle.
Let’s use UMAP with the default parameters and see what the plot looks like!
# Run UMAP with default parameters, but perform PCA first and use the specified
# number of PCs. This is recommended for > 100 columns. 50 PCs are often
# recommended for t-SNE applications, which we'll see in a moment.
umap_results <- uwot::umap(high_var_mat, pca = 50)
# Add the subtype labels to the coordinates returned by uwot::umap
umap_df <- data.frame(umap_results) %>%
rename(UMAP1 = X1, UMAP2 = X2) %>%
bind_cols(subtype_df)
# Make a plot that's very similar to the one we made for PC1 and PC2
ggplot(umap_df,
aes(x = UMAP1,
y = UMAP2,
color = molecular_subtype)) +
geom_point(alpha = 0.75) +
colorblindr::scale_color_OkabeIto() +
theme_bw()

Now that we have an idea of what a UMAP plot with the default parameters looks like, let’s try experimenting with the n_neighbors parameter (number of nearest neighbors). This parameter constrains the size of the local neighborhood size when learning the manifold structure. Lower values emphasize the local structure of the data (ref).
In the interest of experimenting with the n_neighbors parameter, we’ve written a wrapper function that will allow us to
# For a given n_neighbor value, run UMAP and make a plot using the coordinates
# it returns with samples colored by their subtype label.
umap_plot_wrapper <- function(n_neighbors_param) {
# Perform UMAP using the specified n_neighbors value
umap_results <- uwot::umap(high_var_mat,
n_neighbors = n_neighbors_param,
pca = 50)
# Add the subtype labels
umap_df <- data.frame(umap_results) %>%
rename(UMAP1 = X1, UMAP2 = X2) %>%
bind_cols(subtype_df)
# Make a plot that's very similar to the one we made for PC1 and PC2
ggplot(umap_df,
aes(x = UMAP1,
y = UMAP2,
color = molecular_subtype)) +
geom_point(alpha = 0.75) +
colorblindr::scale_color_OkabeIto() +
theme_bw()
}
Let’s start by running UMAP using the same n_neighbors value but a different seed.
# Set a different seed but use the same n_neighbors
set.seed(1234)
umap_plot_wrapper(n_neighbors_param = 15)

For visualization, this difference doesn’t change our interpretation, but it’s a good idea to run this multiple times with different seeds to make sure you’re not getting anomalous results.
Okay, let’s try a variety of n_neighbor values and see what happens.
umap_plot_wrapper(n_neighbors_param = 3)

umap_plot_wrapper(n_neighbors_param = 30)

umap_plot_wrapper(n_neighbors_param = 50)

t-Distributed Stochastic Neighbor Embedding (t-SNE)
Like UMAP, t-SNE is nonlinear dimensionality technique that can produce well-separated clusters.
# This implementation performs PCA first.
# We are using the default perplexity value here, but like we saw with
# n_neighbors this parameter is highly influential on your results!
tsne_results <- Rtsne::Rtsne(high_var_mat, perplexity = 30)
# Add the subtype labels to the coordinates returned by Rtsne::Rtsne()
tsne_df <- data.frame(tsne_results$Y) %>%
rename(tSNE1 = X1, tSNE2 = X2) %>%
bind_cols(subtype_df)
# Make a plot using the coordinates from t-SNE
ggplot(tsne_df,
aes(x = tSNE1,
y = tSNE2,
color = molecular_subtype)) +
geom_point(alpha = 0.75) +
colorblindr::scale_color_OkabeIto() +
theme_bw()

Some takeaways!
- Newer methods that learn nonlinear patterns can help us find well-separated clusters, which is handy for visualization.
- The results strongly depend on parameters that balance local structure vs. global structure, so these values should be chosen based on the question on hand and it may be useful to test a range of values.
- The interpretation of these methods may sometimes be counter-intuitive (e.g., distance between clusters).
A domain-specific dimension reduction technique: Pathway-Level Information ExtractoR (PLIER)
Adapted from CCDL OpenPBTA PLIER and Plotting latent variables notebooks.
All of the techniques we’ve seen so far can (can, not should!) be applied to any generic set of data. Now we will cover a method called Pathway-Level Information Extractor (PLIER) (Mao et al. (2019)).
Let’s revisit the example from above and put it in the context of PLIER:

We like PLIER for a few reasons:
- It is a matrix factorization approach. That means we can get a low-dimensional representation of our data. Specifically, PLIER learns correlated patterns of expression in our data or latent variables (LVs). Here, a latent variable is an “eigengene-like” combination of genes’ expression. (It’s called latent because it’s not directly measured, but instead inferred from the individual gene measurements.)
- It includes penalties such that some of the LVs will align with gene sets that we give it, so it’s excellent for biological discovery.
- The authors demonstrated that it performs favorably with regard to estimating proportion of immune cells in a sample as compared to other methods.
- Because not all LVs align with the gene sets we input, some of them capture unwanted technical variation. In our experience with the method, it does this quite well.
Here’s an overview of the PLIER method from Mao et al. (2019) (Figure 1).

Fig. 1 | PLIER overview. PLIER is a matrix factorization approach that decomposes gene expression data into a product of a small number of LVs and their corresponding gene associations or loadings, while constraining the loadings to align with the most relevant automatically selected subset of prior knowledge. a, Given two inputs, the gene expression matrix Y and the prior knowledge (represented as binary gene set membership in matrix C), the method returns the LVs (B), their loadings (Z), and an additional sparse matrix (U) that specifies which (if any) prior-information gene sets and pathways are used for each LV. The light gray area of U indicates the large number of zero elements of the matrix. We apply our method to a whole-blood human gene expression dataset. b, The positive entries of the resulting U matrix are visualized as a heat map, facilitating the identification of the correspondence between specific LVs and prior biological knowledge. As the absolute scale of the U matrix is arbitrary, each column is normalized to a maximum of 1. c, We validate the LVs mapped to specific leukocyte cell types by comparing PLIER estimated relative cell-type proportions with direct measurements by mass cytometry. Dashed lines represent 0.05, 0.01, and 0.001 significance levels for Spearman rank correlation (one-tailed test). NK cell, natural killer cell.
In the interest of time, we’ve prepared the model in advance that we will read in now. You can see the steps we took in setup/02-plier-prep.Rmd.
plier_results <- read_rds(plier_file)
PLIER gives us information about the association between a latent variable it learns and an input gene set in the summary data frame. In the interest of time, we’re going to focus in on a single latent variable to illustrate how you could use these values.
plier_results$summary %>%
filter(`LV index` == 56,
FDR < 0.05) %>%
arrange(FDR)
The B matrix contains the estimates or expression values for all of the latent variables learned by the model.
dim(plier_results$B)
[1] 58 121
Let’s get this into long format and add the subtype labels.
lv_df <- data.frame(plier_results$B) %>%
# Data frame where the latent variable names are in a column
rownames_to_column(var = "LV") %>%
# Wide to long
pivot_longer(cols = starts_with("BS_"),
names_to = "Kids_First_Biospecimen_ID",
values_to = "LV_estimate") %>%
# Add the subtype labels
inner_join(subtype_df, by = "Kids_First_Biospecimen_ID")
And plot the estimates for LV56 – we can use this to get an idea about the differences in immune-related signals between the subtypes.
lv_df %>%
# PLIER names the latent variables based on the gene sets that they're
# associated with, but as we saw above this is not the only gene set that's
# associated with this LV!
filter(LV == "56,SVM Macrophages M2") %>%
# Make a boxplot where samples are grouped by their molecular subtype and
# that shows the individual samples as points
ggplot(aes(x = molecular_subtype,
y = LV_estimate,
group = molecular_subtype,
colour = molecular_subtype)) +
geom_boxplot(outlier.shape = NA) +
geom_jitter(width = 0.2, alpha = 0.5) +
# Use the same theme and color scheme we have been using throughout
theme_bw() +
colorblindr::scale_color_OkabeIto()

Some takeaways!
- Depending on the goals of our analysis, we may want to select a domain-specific methodology.
- We’ve reduced the dimensions of our data from 10s of 1000s of genes to 58 latent variables that we could test for differences between subtypes.
Other resources
- This website explains PCA visually.
- Wattenberg et al. (2016) discusses how to use t-SNE properly with great visuals. (The lessons apply to UMAP as well, with a broad substitution of the
n_neighbors parameter for perplexity.)
- Nguyen & Holmes (2019) lay out guidelines on choosing dimensions reduction methods.
- Freytag (2019) is a nice explanation and comparison of many different dimensionality reduction techniques that you may encounter.
- Raj (2019) - another nice introduction to dimensionality reduction
Session Info
sessionInfo()
R version 4.0.2 (2020-06-22)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04 LTS
Matrix products: default
BLAS/LAPACK: /usr/lib/x86_64-linux-gnu/openblas-openmp/libopenblasp-r0.3.8.so
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=C LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] Rtsne_0.15 uwot_0.1.8 Matrix_1.2-18 forcats_0.5.0 stringr_1.4.0 dplyr_1.0.0 purrr_0.3.4
[8] readr_1.3.1 tidyr_1.1.0 tibble_3.0.3 ggplot2_3.3.2 tidyverse_1.3.0
loaded via a namespace (and not attached):
[1] tinytex_0.25 tidyselect_1.1.0 xfun_0.16 haven_2.3.1 lattice_0.20-41 colorspace_1.4-1
[7] vctrs_0.3.2 generics_0.0.2 htmltools_0.5.0 yaml_2.2.1 base64enc_0.1-3 blob_1.2.1
[13] rlang_0.4.7 pillar_1.4.6 withr_2.2.0 glue_1.4.1 DBI_1.1.0 dbplyr_1.4.4
[19] modelr_0.1.8 readxl_1.3.1 matrixStats_0.56.0 lifecycle_0.2.0 munsell_0.5.0 gtable_0.3.0
[25] cellranger_1.1.0 rvest_0.3.6 evaluate_0.14 labeling_0.3 knitr_1.29 irlba_2.3.3
[31] fansi_0.4.1 broom_0.7.0 Rcpp_1.0.5 scales_1.1.1 backports_1.1.8 jsonlite_1.7.0
[37] RSpectra_0.16-0 FNN_1.1.3 farver_2.0.3 fs_1.5.0 hms_0.5.3 digest_0.6.25
[43] stringi_1.4.6 grid_4.0.2 cli_2.0.2 colorblindr_0.1.0 tools_4.0.2 magrittr_1.5
[49] crayon_1.3.4 pkgconfig_2.0.3 ellipsis_0.3.1 xml2_1.3.2 reprex_0.3.0 lubridate_1.7.9
[55] assertthat_0.2.1 rmarkdown_2.3 httr_1.4.2 rstudioapi_0.11 R6_2.4.1 compiler_4.0.2
LS0tCnRpdGxlOiAiRGltZW5zaW9uIHJlZHVjdGlvbiBhbmQgcmVwcmVzZW50YXRpb24gbGVhcm5pbmciCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCmRhdGU6IDIwMjAKLS0tCgpfQWRhcHRlZCBmcm9tIENoaWxkaG9vZCBDYW5jZXIgRGF0YSBMYWIsIEFsZXgncyBMZW1vbmFkZSBTdGFuZCBGb3VuZGF0aW9uIFt0cmFpbmluZyBtYXRlcmlhbHNdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL3RyYWluaW5nLW1vZHVsZXMvdHJlZS9kZGM5Yzc2ZTAzYWFmMTQ3ZmIyZjAxMTQ5MGVhNmFlYzgzNWRlMDY0KS5fCgojIyBCYWNrZ3JvdW5kCgpCZWZvcmUgd2UgZ2V0IGludG8gaXQsIGxldCdzIGJyaWVmbHkgY292ZXIgc29tZSBjb25jZXB0cy4gCgoqKkRpbWVuc2lvbiByZWR1Y3Rpb24qKiAoYWxzbyBjYWxsZWQgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uKSBpcyBhbnkgdGVjaG5pcXVlIHRoYXQgcmVkdWNlcyB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzIG9yIGRpbWVuc2lvbiBpbiBhIGRhdGFzZXQuIApBIGRpbWVuc2lvbiBpbiBvdXIgaGlnaC1kaW1lbnNpb25hbCBzcGFjZSBpcyB0aGUgbWVhc3VyZW1lbnQgb2YgYW4gaW5kaXZpZHVhbCBnZW5lIGluIG91ciBSTkEtc2VxIGRhdGEgaW4gb3VyIHBhcnRpY3VsYXIgY2FzZS4KRGltZW5zaW9uIHJlZHVjdGlvbiBjYW4gY29tZSBhYm91dCB0aHJvdWdoIF9mZWF0dXJlIGVsaW1pbmF0aW9uIG9yIGZlYXR1cmUgc2VsZWN0aW9uXywgd2hlcmUgd2UgcmVtb3ZlIGZlYXR1cmVzIG9yIHZhcmlhYmxlcyBpZiB0aGV5IGFyZSB1bmxpa2VseSB0byBnaXZlIHVzIG1lYW5pbmdmdWwgaW5mb3JtYXRpb24gKG9yIGFyZSByZWR1bmRhbnQgd2l0aCBvdGhlciB2YXJpYWJsZXMpLiAKT3Igd2UgY2FuIHVzZSBfZmVhdHVyZSBleHRyYWN0aW9uXywgd2hlcmUgd2UgZm9ybSBuZXcgZmVhdHVyZXMgZnJvbSB0aGUgaW5wdXQgZGF0YS4KKFNlZSBbRnJleXRhZyAoMjAxOSldKGh0dHBzOi8vcnB1YnMuY29tL1Nhc2tpYS81MjAyMTYpIGFuZCBbUmFqICgyMDE5KV0oaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL2RpbWVuc2lvbmFsaXR5LXJlZHVjdGlvbi1mb3ItbWFjaGluZS1sZWFybmluZy04MGE0NmMyZWJiN2UpLikKCkluIGdlbmVyYWwsIHRoZSBpZGVhIGJlaGluZCBkaW1lbnNpb24gcmVkdWN0aW9uLCBhcyB3ZSdsbCBjb3ZlciBpdCBpbiB0aGlzIG5vdGVib29rLCBpcyB0aGF0IHdlIHdhbnQgdG8gdHJhbnNmb3JtIG91ciBpbnB1dCBkYXRhIGludG8gYSBsb3ctZGltZW5zaW9uYWwgcmVwcmVzZW50YXRpb24gdGhhdCByZXRhaW5zIHNvbWUgb2YgdGhlIG1lYW5pbmdmdWwgc3RydWN0dXJlIChfZmVhdHVyZSBleHRyYWN0aW9uXykuIAoKSWYgd2Ugd2FudCB0byBnZXQgdGhlIHNlbnNlIG9mIHRoZSBvdmVyYWxsIHN0cnVjdHVyZSBpbiBvdXIgZGF0YXNldCwgc29tZXRpbWVzIHdlIHdhbnQgdGhlIGZvbGxvd2luZzoKCiFbXShkaWFncmFtcy8yLWRpbS1leGFtcGxlLnBuZykKCioqUmVwcmVzZW50YXRpb24gbGVhcm5pbmcqKiAoYWxzbyBjYWxsZWQgZmVhdHVyZSBlbmdpbmVlcmluZyBvciBmZWF0dXJlIGxlYXJuaW5nKSBpcyBhbnkgdGVjaG5pcXVlIHRoYXQgYXV0b21hdGljYWxseSBkZXRlY3RzIHJlcHJlc2VudGF0aW9ucyBmcm9tIGlucHV0IGRhdGEuIApXZSdsbCBjb3ZlciB1bnN1cGVydmlzZWQgbWV0aG9kcyBsaWtlIG1hdHJpeCBmYWN0b3JpemF0aW9uIGhlcmUsIGJ1dCBzdXBlcnZpc2VkIG1ldGhvZHMgbGlrZSBzdXBlcnZpc2VkIG5ldXJhbCBuZXR3b3JrcyBhcmUgZXhhbXBsZXMgb2YgcmVwcmVzZW50YXRpb24gbGVhcm5pbmcsIHRvby4KCldoZW4gd2UncmUgd29ya2luZyB3aXRoIGdlbmUgZXhwcmVzc2lvbiBkYXRhLCBvZnRlbiB3aGF0IHdlIHdhbnQgaXMgdGhlIGZvbGxvd2luZzoKCiFbXShkaWFncmFtcy9iaW9sb2dpY2FsbHktbWVhbmluZ2Z1bC5wbmcpCgpXZSBtYXkgd2FudCB0byB1c2UgdGhlc2UgcmVwcmVzZW50YXRpb25zIGluIGEgc2VtaS1zdXBlcnZpc2VkIG1hbm5lciwgd2hlcmUgd2UgbGVhcm4gcmVwcmVzZW50YXRpb25zIGZyb20gYSBjb2xsZWN0aW9uIG9mIGRhdGEgd2l0aG91dCBsYWJlbHMgYW5kIHRoZW4gdXNlIHRoZW0gdG8gZW5jb2RlIGEgbG93LWRpbWVuc2lvbmFsIHJlcHJlc2VudGF0aW9uIG9mIGRhdGEgd2l0aCBsYWJlbHMgdGhhdCB3ZSB0aGVuIHVzZSBmb3IgcHJlZGljdGlvbi4KCiMjIFNldCB1cAoKYGBge3J9CiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgyMDIwKQpgYGAKCiMjIyBMaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKIyBVTUFQIGltcGxlbWVudGF0aW9uCmxpYnJhcnkodXdvdCkKIyB0LVNORSBpbXBsZW1lbnRhdGlvbgpsaWJyYXJ5KFJ0c25lKQpgYGAKIyMjIEZpbGVzCgpgYGB7cn0KY2xpbmljYWxfZmlsZSA8LSBmaWxlLnBhdGgoImRhdGEiLCAicGJ0YS1tZWR1bGxvLWhpc3RvbG9naWVzLnRzdi5neiIpCgojIFByb2Nlc3NlZCBSTkEtc2VxIGRhdGEKcm5hc2VxX2ZpbGUgPC0gZmlsZS5wYXRoKCJkYXRhIiwgInBidGEtbWVkdWxsby12c3QtY29sbGFwc2VkLnRzdi5neiIpCgojIFBhdGh3YXktTGV2ZWwgSW5mb3JtYXRpb24gRXh0cmFjdG9SIG1vZGVsIHdlJ3ZlIHByZXBhcmVkIGFoZWFkIG9mIHRpbWUKcGxpZXJfZmlsZSA8LSBmaWxlLnBhdGgoIm1vZGVscyIsICJwYnRhLW1lZHVsbG8tcGxpZXIuUkRTIikKYGBgCgoKIyMgUmVhZCBpbiBkYXRhCgpSZWFkIGluIG91ciB0cmFuc2Zvcm1lZCBSTkEtc2VxIGRhdGEuCgpgYGB7cn0Kcm5hc2VxX2RmIDwtIHJlYWRfdHN2KHJuYXNlcV9maWxlKQpgYGAKCkZvciB0aGUgYXBwbGljYXRpb25zIGluIHRoaXMgbm90ZWJvb2ssIHdlJ2xsIHRyYW5zcG9zZSBSTkEtc2VxIGRhdGEuCgpgYGB7cn0Kcm5hc2VxX21hdCA8LSBybmFzZXFfZGYgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJnZW5lX3N5bWJvbCIpICU+JQogIGFzLm1hdHJpeCgpICU+JQogIHQoKQpgYGAKCkJlY2F1c2Ugd2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIG92ZXJhbGwgc3RydWN0dXJlIG9mIG91ciBkYXRhLCB3ZSdyZSBnb2luZyB0byBzdWJzZXQgdGhlIG1hdHJpeCB0byBmZWF0dXJlcyB3aXRoIGhpZ2ggdmFyaWFuY2UgbW92aW5nIGZvcndhcmQuCkVsaW1pbmF0aW5nIGZlYXR1cmVzIHRoYXQgKmRvbid0KiB2YXJ5IHdpdGhpbiB0aGVtc2VsdmVzIGFuZCB0aGVyZWZvcmUgYXJlIGxlc3MgbGlrZWx5IHRvIGNvbnRyaWJ1dGUgdG8gdGhlIG92ZXJhbGwgc3RydWN0dXJlIGluIG91ciBkYXRhIHRoYXQgd2UgYXJlIG1vc3QgaW50ZXJlc3RlZCBpbi4KRmV3ZXIgZmVhdHVyZXMgaXMgYXBwZWFsaW5nIGZyb20gYSBjb21wdXRhdGlvbmFsIHJlc291cmNlIHN0YW5kcG9pbnQhCgpgYGB7cn0KIyBHZW5lcyBhcmUgbm93IGNvbHVtbnMKZ2VuZV92YXJpYW5jZSA8LSBtYXRyaXhTdGF0czo6Y29sVmFycyhybmFzZXFfbWF0KQojIFdlJ2xsIHdhbnQgdG8gcmV0YWluIHRoZSB0b3AgMTAlCnZhcmlhbmNlX3RocmVzaG9sZCA8LSBxdWFudGlsZShnZW5lX3ZhcmlhbmNlLCAwLjk1KQojIEZpbHRlciB0byB0b3AgMTAlCmhpZ2hfdmFyX21hdCA8LSBybmFzZXFfbWF0WywgZ2VuZV92YXJpYW5jZSA+IHZhcmlhbmNlX3RocmVzaG9sZF0KYGBgCgpSZWFkIGluIG91ciBjbGluaWNhbCBtZXRhZGF0YSBmaWxlLgoKYGBge3J9CmNsaW5pY2FsX2RmIDwtIHJlYWRfdHN2KGNsaW5pY2FsX2ZpbGUpCmBgYAoKRm9yIHRoZSBtb3N0IHBhcnQsIHdlJ2xsIG9ubHkgdXNlIHRoZSBtb2xlY3VsYXIgc3VidHlwZSBpbmZvcm1hdGlvbiBmcm9tIHRoZSBjbGluaWNhbCBtZWF0YWRhdGEuCgpgYGB7cn0Kc3VidHlwZV9kZiA8LSBjbGluaWNhbF9kZiAlPiUKICBzZWxlY3QoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgbW9sZWN1bGFyX3N1YnR5cGUpCmBgYAoKCiMjIERpbWVuc2lvbiByZWR1Y3Rpb24KCl9BZGFwdGVkIGZyb20gW0NDREwgc2NSTkEtc2VxIGRpbWVuc2lvbiByZWR1Y3Rpb25dKGh0dHBzOi8vaHRtbHByZXZpZXcuZ2l0aHViLmlvLz9odHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS90cmFpbmluZy1tb2R1bGVzL2Jsb2IvMjAyMC1qdWx5L3NjUk5BLXNlcS8wNS1kaW1lbnNpb25fcmVkdWN0aW9uX3NjUk5BLXNlcS5uYi5odG1sKSBhbmQgW0RpbWVuc2lvbmFsaXR5IFJlZHVjdGlvbiBjb25jZXB0c10oaHR0cHM6Ly9hbGV4c2xlbW9uYWRlLmdpdGh1Yi5pby8yMDIwLWp1bHktdHJhaW5pbmcvc3VwcGxlbWVudGFsX25vdGVib29rcy8yMDIwLTA3LTI5X2RpbWVuc2lvbmFsaXR5X3JlZHVjdGlvbi5uYi5odG1sKV8KCiMjIyBQcmluY2lwYWwgQ29tcG9uZW50cyBBbmFseXNpcyAoUENBKQoKUHJpbmNpcGFsIGNvbXBvbmVudHMgYW5hbHlzaXMgKFBDQSkgaXMgYSBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gdGVjaG5pcXVlIHRoYXQgY2FwdHVyZXMgdGhlIG1haW4gc291cmNlcyBvZiB2YXJpYXRpb24gaW4gb3VyIGRhdGEgaW4gdGhlIGZpcnN0IHR3byBwcmluY2lwYWwgY29tcG9uZW50cyAoUEMxIGFuZCBQQzIpLgpQQ0EgYXBwbGllcyBsaW5lYXIgYWxnZWJyYSB0byBmaW5kIHRoZSBheGlzIG9mIGdyZWF0ZXN0IHZhcmlhdGlvbiBpbiB0aGUgZGF0YSBtYXRyaXgsIHRoZW4gdGhlIGF4aXMgcGVycGVuZGljdWxhciB0byB0aGF0IHdpdGggdGhlIG1vc3QgcmVtYWluaW5nIHZhcmlhdGlvbiwgdGhlbiB0aGUgb25lIHBlcnBlbmRpY3VsYXIgdG8gdGhvc2Ugd2l0aCB0aGUgbW9zdCByZW1haW5pbmcsIGV0Yy4KCmBgYHtyfQojIFBDQQpwY2FfcmVzdWx0cyA8LSBwcmNvbXAoaGlnaF92YXJfbWF0LCBzY2FsZSA9IFRSVUUpCgojIEpvaW4gdGhlIGZpcnN0IHR3byBQQ3Mgd2l0aCB0aGUgc3VidHlwZSBsYWJlbHMgZm9yIHBsb3R0aW5nIHB1cnBvc2VzCnBjYV9kZiA8LSBkYXRhLmZyYW1lKHBjYV9yZXN1bHRzJHhbLCAxOjJdKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiKSAlPiUKICBpbm5lcl9qb2luKHN1YnR5cGVfZGYsIGJ5ID0gIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiKQoKIyBDcmVhdGUgYSBwbG90IG9mIHRoZSBmaXJzdCB0d28gUENzCmdncGxvdChwY2FfZGYsCiAgICAgICBhZXMoeCA9IFBDMSwgCiAgICAgICAgICAgeSA9IFBDMiwKICAgICAgICAgICBjb2xvciA9IG1vbGVjdWxhcl9zdWJ0eXBlKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjc1KSArCiAgY29sb3JibGluZHI6OnNjYWxlX2NvbG9yX09rYWJlSXRvKCkgKwogIHRoZW1lX2J3KCkKYGBgCgpQQ0EgY2FuIHNvbWV0aW1lcyBwcm9kdWNlIHJlc3VsdHMgd2hlcmUgdGhlIHBvaW50cyBvY2N1cHkgdGhlIHdob2xlIHBsb3R0aW5nIHNwYWNlLCB3aGljaCB3b3VsZCBsaWtlbHkgYmUgbW9yZSBwcm9ub3VuY2VkIGlmIHdlIGhhZCBldmVuIG1vcmUgc2FtcGxlcy4KCiMjIyBVbmlmb3JtIE1hbmlmb2xkIEFwcHJveGltYXRpb24gYW5kIFByb2plY3Rpb24gKFVNQVApCgoqKlVNQVAqKiAoVW5pZm9ybSBNYW5pZm9sZCBBcHByb3hpbWF0aW9uIGFuZCBQcm9qZWN0aW9uKSBpcyBhIG1hY2hpbmUgbGVhcm5pbmcgdGVjaG5pcXVlIGRlc2lnbmVkIHRvIHByb3ZpZGUgbW9yZSBkZXRhaWwgaW4gaGlnaGx5IGRpbWVuc2lvbmFsIGRhdGEgdGhhbiBhIHR5cGljYWwgUENBLiAKV2hpbGUgUENBIGFzc3VtZXMgdGhhdCB0aGUgdmFyaWF0aW9uIHdlIGNhcmUgYWJvdXQgaGFzIGEgcGFydGljdWxhciBkaXN0cmlidXRpb24gKG5vcm1hbCwgYnJvYWRseSBzcGVha2luZyksIFVNQVAgYWxsb3dzIG1vcmUgY29tcGxpY2F0ZWQgZGlzdHJpYnV0aW9ucyB0aGF0IGl0IGxlYXJucyBmcm9tIHRoZSBkYXRhLiAKV2Ugd29uJ3QgZ28gaW50byB0aGUgdW5kZXJseWluZyBtYXRoZW1hdGljcywgYnV0IHlvdSBjYW4gbG9vayBhdCB0aGUgcGFwZXIgYnkgW01jSW5uZXMsIEhlYWx5LCAmIE1lbHZpbGxlICgyMDE4KV0oaHR0cHM6Ly9hcnhpdi5vcmcvYWJzLzE4MDIuMDM0MjYpLiAKVGhlIG1haW4gYWR2YW50YWdlIG9mIHRoaXMgY2hhbmdlIGluIHVuZGVybHlpbmcgYXNzdW1wdGlvbnMgaXMgdGhhdCBVTUFQIGNhbiBkbyBhIGJldHRlciBqb2Igc2VwYXJhdGluZyBjbHVzdGVycywgZXNwZWNpYWxseSB3aGVuIHNvbWUgb2YgdGhvc2UgY2x1c3RlcnMgbWF5IGJlIG1vcmUgc2ltaWxhciB0byBlYWNoIG90aGVyIHRoYW4gb3RoZXJzLiAgCgpBbm90aGVyIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiB0ZWNobmlxdWUgdGhhdCB5b3UgbWF5IGhhdmUgaGVhcmQgb2YgaXMgKip0LVNORSoqICh0LWRpc3RyaWJ1dGVkIFN0b2NoYXN0aWMgTmVpZ2hib3IgRW1iZWRkaW5nKSwgd2hpY2ggaGFzIHNpbWlsYXIgcHJvcGVydGllcyB0byBVTUFQLCBhbmQgb2Z0ZW4gcHJvZHVjZXMgc2ltaWxhciByZXN1bHRzLiAKVGhlcmUgaXMgc29tZSBvbmdvaW5nIGRlYmF0ZSBhYm91dCB3aGljaCBvZiB0aGVzZSB0d28gdGVjaG5pcXVlcyBpcyBzdXBlcmlvciBmb3IgaGlnaC1kaW1lbnNpb25hbCBiaW9sb2dpY2FsIGRhdGEsIGFuZCB3aGV0aGVyIHRoZSBkaWZmZXJlbmNlcyBhcmUgZHVlIHRvIHRoZSB1bmRlcmx5aW5nIGFsZ29yaXRobSBvciB0byBpbXBsZW1lbnRhdGlvbiBhbmQgcGFyYW1ldGVyIGluaXRpYWxpemF0aW9uIGRlZmF1bHRzLiAKUmVnYXJkbGVzcyBvZiB3aHksIGluIG91ciBleHBlcmllbmNlLCBVTUFQIHNlZW1zIHRvIHByb2R1Y2Ugc2xpZ2h0bHkgYmV0dGVyIHJlc3VsdHMgaW4gdGhpcyBjb250ZXh0IGFuZCBydW4gYSBiaXQgZmFzdGVyLCBidXQgdGhlIGRpZmZlcmVuY2VzIGNhbiBiZSBzdWJ0bGUuCgpMZXQncyB1c2UgVU1BUCB3aXRoIHRoZSBkZWZhdWx0IHBhcmFtZXRlcnMgYW5kIHNlZSB3aGF0IHRoZSBwbG90IGxvb2tzIGxpa2UhCgpgYGB7cn0KIyBSdW4gVU1BUCB3aXRoIGRlZmF1bHQgcGFyYW1ldGVycywgYnV0IHBlcmZvcm0gUENBIGZpcnN0IGFuZCB1c2UgdGhlIHNwZWNpZmllZAojIG51bWJlciBvZiBQQ3MuIFRoaXMgaXMgcmVjb21tZW5kZWQgZm9yID4gMTAwIGNvbHVtbnMuIDUwIFBDcyBhcmUgb2Z0ZW4gCiMgcmVjb21tZW5kZWQgZm9yIHQtU05FIGFwcGxpY2F0aW9ucywgd2hpY2ggd2UnbGwgc2VlIGluIGEgbW9tZW50Lgp1bWFwX3Jlc3VsdHMgPC0gdXdvdDo6dW1hcChoaWdoX3Zhcl9tYXQsIHBjYSA9IDUwKQoKIyBBZGQgdGhlIHN1YnR5cGUgbGFiZWxzIHRvIHRoZSBjb29yZGluYXRlcyByZXR1cm5lZCBieSB1d290Ojp1bWFwCnVtYXBfZGYgPC0gZGF0YS5mcmFtZSh1bWFwX3Jlc3VsdHMpICU+JQogIHJlbmFtZShVTUFQMSA9IFgxLCBVTUFQMiA9IFgyKSAlPiUKICBiaW5kX2NvbHMoc3VidHlwZV9kZikKCiMgTWFrZSBhIHBsb3QgdGhhdCdzIHZlcnkgc2ltaWxhciB0byB0aGUgb25lIHdlIG1hZGUgZm9yIFBDMSBhbmQgUEMyCmdncGxvdCh1bWFwX2RmLAogICAgICAgYWVzKHggPSBVTUFQMSwgCiAgICAgICAgICAgeSA9IFVNQVAyLAogICAgICAgICAgIGNvbG9yID0gbW9sZWN1bGFyX3N1YnR5cGUpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNzUpICsKICBjb2xvcmJsaW5kcjo6c2NhbGVfY29sb3JfT2thYmVJdG8oKSArCiAgdGhlbWVfYncoKQpgYGAKCk5vdyB0aGF0IHdlIGhhdmUgYW4gaWRlYSBvZiB3aGF0IGEgVU1BUCBwbG90IHdpdGggdGhlIGRlZmF1bHQgcGFyYW1ldGVycyBsb29rcyBsaWtlLCBsZXQncyB0cnkgZXhwZXJpbWVudGluZyB3aXRoIHRoZSBgbl9uZWlnaGJvcnNgIHBhcmFtZXRlciAobnVtYmVyIG9mIG5lYXJlc3QgbmVpZ2hib3JzKS4gClRoaXMgcGFyYW1ldGVyIGNvbnN0cmFpbnMgdGhlIHNpemUgb2YgdGhlIGxvY2FsIG5laWdoYm9yaG9vZCBzaXplIHdoZW4gbGVhcm5pbmcgdGhlIG1hbmlmb2xkIHN0cnVjdHVyZS4gCkxvd2VyIHZhbHVlcyBlbXBoYXNpemUgdGhlIGxvY2FsIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSAoW3JlZl0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3V3b3QvdmVyc2lvbnMvMC4xLjgvdG9waWNzL3VtYXApKS4KCkluIHRoZSBpbnRlcmVzdCBvZiBleHBlcmltZW50aW5nIHdpdGggdGhlIGBuX25laWdoYm9yc2AgcGFyYW1ldGVyLCB3ZSd2ZSB3cml0dGVuIGEgd3JhcHBlciBmdW5jdGlvbiB0aGF0IHdpbGwgYWxsb3cgdXMgdG8gCgpgYGB7cn0KIyBGb3IgYSBnaXZlbiBuX25laWdoYm9yIHZhbHVlLCBydW4gVU1BUCBhbmQgbWFrZSBhIHBsb3QgdXNpbmcgdGhlIGNvb3JkaW5hdGVzCiMgaXQgcmV0dXJucyB3aXRoIHNhbXBsZXMgY29sb3JlZCBieSB0aGVpciBzdWJ0eXBlIGxhYmVsLgp1bWFwX3Bsb3Rfd3JhcHBlciA8LSBmdW5jdGlvbihuX25laWdoYm9yc19wYXJhbSkgewogIAogICMgUGVyZm9ybSBVTUFQIHVzaW5nIHRoZSBzcGVjaWZpZWQgbl9uZWlnaGJvcnMgdmFsdWUKICB1bWFwX3Jlc3VsdHMgPC0gdXdvdDo6dW1hcChoaWdoX3Zhcl9tYXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fbmVpZ2hib3JzID0gbl9uZWlnaGJvcnNfcGFyYW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGNhID0gNTApCgogICMgQWRkIHRoZSBzdWJ0eXBlIGxhYmVscwogIHVtYXBfZGYgPC0gZGF0YS5mcmFtZSh1bWFwX3Jlc3VsdHMpICU+JQogICAgcmVuYW1lKFVNQVAxID0gWDEsIFVNQVAyID0gWDIpICU+JQogICAgYmluZF9jb2xzKHN1YnR5cGVfZGYpCiAgCiAgIyBNYWtlIGEgcGxvdCB0aGF0J3MgdmVyeSBzaW1pbGFyIHRvIHRoZSBvbmUgd2UgbWFkZSBmb3IgUEMxIGFuZCBQQzIKICBnZ3Bsb3QodW1hcF9kZiwKICAgICAgICAgYWVzKHggPSBVTUFQMSwgCiAgICAgICAgICAgICB5ID0gVU1BUDIsCiAgICAgICAgICAgICBjb2xvciA9IG1vbGVjdWxhcl9zdWJ0eXBlKSkgKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNzUpICsKICAgIGNvbG9yYmxpbmRyOjpzY2FsZV9jb2xvcl9Pa2FiZUl0bygpICsKICAgIHRoZW1lX2J3KCkKfQpgYGAKCkxldCdzIHN0YXJ0IGJ5IHJ1bm5pbmcgVU1BUCB1c2luZyB0aGUgc2FtZSBgbl9uZWlnaGJvcnNgIHZhbHVlIGJ1dCBhIGRpZmZlcmVudCBzZWVkLgoKYGBge3J9CiMgU2V0IGEgZGlmZmVyZW50IHNlZWQgYnV0IHVzZSB0aGUgc2FtZSBuX25laWdoYm9ycwpzZXQuc2VlZCgxMjM0KQp1bWFwX3Bsb3Rfd3JhcHBlcihuX25laWdoYm9yc19wYXJhbSA9IDE1KQpgYGAKCkZvciB2aXN1YWxpemF0aW9uLCB0aGlzIGRpZmZlcmVuY2UgZG9lc24ndCBjaGFuZ2Ugb3VyIGludGVycHJldGF0aW9uLCBidXQgaXQncyBhIGdvb2QgaWRlYSB0byBydW4gdGhpcyBtdWx0aXBsZSB0aW1lcyB3aXRoIGRpZmZlcmVudCBzZWVkcyB0byBtYWtlIHN1cmUgeW91J3JlIG5vdCBnZXR0aW5nIGFub21hbG91cyByZXN1bHRzLgoKT2theSwgbGV0J3MgdHJ5IGEgdmFyaWV0eSBvZiBgbl9uZWlnaGJvcmAgdmFsdWVzIGFuZCBzZWUgd2hhdCBoYXBwZW5zLgoKYGBge3J9CnVtYXBfcGxvdF93cmFwcGVyKG5fbmVpZ2hib3JzX3BhcmFtID0gMykKYGBgCgpgYGB7cn0KdW1hcF9wbG90X3dyYXBwZXIobl9uZWlnaGJvcnNfcGFyYW0gPSAzMCkKYGBgCgpgYGB7cn0KdW1hcF9wbG90X3dyYXBwZXIobl9uZWlnaGJvcnNfcGFyYW0gPSA1MCkKYGBgCgojIyMgdC1EaXN0cmlidXRlZCBTdG9jaGFzdGljIE5laWdoYm9yIEVtYmVkZGluZyAodC1TTkUpCgpMaWtlIFVNQVAsIHQtU05FIGlzIG5vbmxpbmVhciBkaW1lbnNpb25hbGl0eSB0ZWNobmlxdWUgdGhhdCBjYW4gcHJvZHVjZSB3ZWxsLXNlcGFyYXRlZCBjbHVzdGVycy4KCmBgYHtyfQojIFRoaXMgaW1wbGVtZW50YXRpb24gcGVyZm9ybXMgUENBIGZpcnN0LgojIFdlIGFyZSB1c2luZyB0aGUgZGVmYXVsdCBwZXJwbGV4aXR5IHZhbHVlIGhlcmUsIGJ1dCBsaWtlIHdlIHNhdyB3aXRoCiMgbl9uZWlnaGJvcnMgdGhpcyBwYXJhbWV0ZXIgaXMgaGlnaGx5IGluZmx1ZW50aWFsIG9uIHlvdXIgcmVzdWx0cyEKdHNuZV9yZXN1bHRzIDwtIFJ0c25lOjpSdHNuZShoaWdoX3Zhcl9tYXQsIHBlcnBsZXhpdHkgPSAzMCkKCiMgQWRkIHRoZSBzdWJ0eXBlIGxhYmVscyB0byB0aGUgY29vcmRpbmF0ZXMgcmV0dXJuZWQgYnkgUnRzbmU6OlJ0c25lKCkKdHNuZV9kZiA8LSBkYXRhLmZyYW1lKHRzbmVfcmVzdWx0cyRZKSAlPiUKICByZW5hbWUodFNORTEgPSBYMSwgdFNORTIgPSBYMikgJT4lCiAgYmluZF9jb2xzKHN1YnR5cGVfZGYpCgojIE1ha2UgYSBwbG90IHVzaW5nIHRoZSBjb29yZGluYXRlcyBmcm9tIHQtU05FCmdncGxvdCh0c25lX2RmLAogICAgICAgYWVzKHggPSB0U05FMSwgCiAgICAgICAgICAgeSA9IHRTTkUyLAogICAgICAgICAgIGNvbG9yID0gbW9sZWN1bGFyX3N1YnR5cGUpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNzUpICsKICBjb2xvcmJsaW5kcjo6c2NhbGVfY29sb3JfT2thYmVJdG8oKSArCiAgdGhlbWVfYncoKQpgYGAKCioqU29tZSB0YWtlYXdheXMhKioKCi0gTmV3ZXIgbWV0aG9kcyB0aGF0IGxlYXJuIG5vbmxpbmVhciBwYXR0ZXJucyBjYW4gaGVscCB1cyBmaW5kIHdlbGwtc2VwYXJhdGVkIGNsdXN0ZXJzLCB3aGljaCBpcyBoYW5keSBmb3IgdmlzdWFsaXphdGlvbi4KLSBUaGUgcmVzdWx0cyBzdHJvbmdseSBkZXBlbmQgb24gcGFyYW1ldGVycyB0aGF0IGJhbGFuY2UgbG9jYWwgc3RydWN0dXJlIHZzLiBnbG9iYWwgc3RydWN0dXJlLCBzbyB0aGVzZSB2YWx1ZXMgc2hvdWxkIGJlIGNob3NlbiBiYXNlZCBvbiB0aGUgcXVlc3Rpb24gb24gaGFuZCBhbmQgaXQgbWF5IGJlIHVzZWZ1bCB0byB0ZXN0IGEgcmFuZ2Ugb2YgdmFsdWVzLgotIFRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGVzZSBtZXRob2RzIG1heSBzb21ldGltZXMgYmUgY291bnRlci1pbnR1aXRpdmUgKGUuZy4sIGRpc3RhbmNlIGJldHdlZW4gY2x1c3RlcnMpLgoKCiMjIEEgZG9tYWluLXNwZWNpZmljIGRpbWVuc2lvbiByZWR1Y3Rpb24gdGVjaG5pcXVlOiBQYXRod2F5LUxldmVsIEluZm9ybWF0aW9uIEV4dHJhY3RvUiAoUExJRVIpCgpfQWRhcHRlZCBmcm9tIFtDQ0RMIE9wZW5QQlRBIFBMSUVSXShodHRwczovL2h0bWxwcmV2aWV3LmdpdGh1Yi5pby8/aHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvdHJhaW5pbmctbW9kdWxlcy9ibG9iLzIwMjAtanVseS9tYWNoaW5lLWxlYXJuaW5nLzAzLW9wZW5wYnRhX1BMSUVSLm5iLmh0bWwpIGFuZCBbUGxvdHRpbmcgbGF0ZW50IHZhcmlhYmxlc10oaHR0cHM6Ly9odG1scHJldmlldy5naXRodWIuaW8vP2h0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL3RyYWluaW5nLW1vZHVsZXMvYmxvYi8yMDIwLWp1bHkvbWFjaGluZS1sZWFybmluZy8wNC1vcGVucGJ0YV9wbG90X0xWLm5iLmh0bWwpIG5vdGVib29rcy5fCgpBbGwgb2YgdGhlIHRlY2huaXF1ZXMgd2UndmUgc2VlbiBzbyBmYXIgY2FuIChjYW4sIG5vdCBzaG91bGQhKSBiZSBhcHBsaWVkIHRvIGFueSBnZW5lcmljIHNldCBvZiBkYXRhLgpOb3cgd2Ugd2lsbCBjb3ZlciBhIG1ldGhvZCBjYWxsZWQgUGF0aHdheS1MZXZlbCBJbmZvcm1hdGlvbiBFeHRyYWN0b3IgKFBMSUVSKSAoW01hbyAqZXQgYWwuKiAoMjAxOSldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L3M0MTU5Mi0wMTktMDQ1Ni0xKSkuCgpMZXQncyByZXZpc2l0IHRoZSBleGFtcGxlIGZyb20gYWJvdmUgYW5kIHB1dCBpdCBpbiB0aGUgY29udGV4dCBvZiBQTElFUjoKCiFbXShkaWFncmFtcy9wbGllci1zdW1tYXJ5LnBuZykKCldlIGxpa2UgUExJRVIgZm9yIGEgZmV3IHJlYXNvbnM6CgotIEl0IGlzIGEgbWF0cml4IGZhY3Rvcml6YXRpb24gYXBwcm9hY2guIAogIFRoYXQgbWVhbnMgd2UgY2FuIGdldCBhIGxvdy1kaW1lbnNpb25hbCByZXByZXNlbnRhdGlvbiBvZiBvdXIgZGF0YS4KICBTcGVjaWZpY2FsbHksIFBMSUVSIGxlYXJucyBjb3JyZWxhdGVkIHBhdHRlcm5zIG9mIGV4cHJlc3Npb24gaW4gb3VyIGRhdGEgb3IgbGF0ZW50IHZhcmlhYmxlcyAoTFZzKS4KICBIZXJlLCBhIGxhdGVudCB2YXJpYWJsZSBpcyBhbiAiZWlnZW5nZW5lLWxpa2UiIGNvbWJpbmF0aW9uIG9mIGdlbmVzJyBleHByZXNzaW9uLgogIChJdCdzIGNhbGxlZCBfbGF0ZW50XyBiZWNhdXNlIGl0J3Mgbm90IGRpcmVjdGx5IG1lYXN1cmVkLCBidXQgaW5zdGVhZCBpbmZlcnJlZCBmcm9tIHRoZSBpbmRpdmlkdWFsIGdlbmUgbWVhc3VyZW1lbnRzLikKLSBJdCBpbmNsdWRlcyBwZW5hbHRpZXMgc3VjaCB0aGF0IF9zb21lXyBvZiB0aGUgTFZzIHdpbGwgYWxpZ24gd2l0aCBnZW5lIHNldHMgdGhhdCB3ZSBnaXZlIGl0LCBzbyBpdCdzIGV4Y2VsbGVudCBmb3IgYmlvbG9naWNhbCBkaXNjb3ZlcnkuCi0gVGhlIGF1dGhvcnMgZGVtb25zdHJhdGVkIHRoYXQgaXQgcGVyZm9ybXMgZmF2b3JhYmx5IHdpdGggcmVnYXJkIHRvIGVzdGltYXRpbmcgcHJvcG9ydGlvbiBvZiBpbW11bmUgY2VsbHMgaW4gYSBzYW1wbGUgYXMgY29tcGFyZWQgdG8gb3RoZXIgbWV0aG9kcy4KLSBCZWNhdXNlIG5vdCBfYWxsXyBMVnMgYWxpZ24gd2l0aCB0aGUgZ2VuZSBzZXRzIHdlIGlucHV0LCBzb21lIG9mIHRoZW0gY2FwdHVyZSB1bndhbnRlZCB0ZWNobmljYWwgdmFyaWF0aW9uLiAKICBJbiBvdXIgZXhwZXJpZW5jZSB3aXRoIHRoZSBtZXRob2QsIGl0IGRvZXMgdGhpcyBxdWl0ZSB3ZWxsLgoKSGVyZSdzIGFuIG92ZXJ2aWV3IG9mIHRoZSBQTElFUiBtZXRob2QgZnJvbSBbTWFvIF9ldCBhbC5fICgyMDE5KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvczQxNTkyLTAxOS0wNDU2LTEpIChGaWd1cmUgMSkuCgohW10oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvdHJhaW5pbmctbW9kdWxlcy9yYXcvZGRjOWM3NmUwM2FhZjE0N2ZiMmYwMTE0OTBlYTZhZWM4MzVkZTA2NC9tYWNoaW5lLWxlYXJuaW5nL2RpYWdyYW1zL21hb19uYXR1cmVfbWV0aG9kc19maWcxLnBuZykKCj4gKipGaWcuIDEgfCBQTElFUiBvdmVydmlldy4qKiAKUExJRVIgaXMgYSBtYXRyaXggZmFjdG9yaXphdGlvbiBhcHByb2FjaCB0aGF0IGRlY29tcG9zZXMgZ2VuZSBleHByZXNzaW9uIGRhdGEgaW50byBhIHByb2R1Y3Qgb2YgYSBzbWFsbCBudW1iZXIgb2YgTFZzIGFuZCB0aGVpciBjb3JyZXNwb25kaW5nIGdlbmUgYXNzb2NpYXRpb25zIG9yIGxvYWRpbmdzLCB3aGlsZSBjb25zdHJhaW5pbmcgdGhlIGxvYWRpbmdzIHRvIGFsaWduIHdpdGggdGhlIG1vc3QgcmVsZXZhbnQgYXV0b21hdGljYWxseSBzZWxlY3RlZCBzdWJzZXQgb2YgcHJpb3Iga25vd2xlZGdlLiAqKmEqKiwgR2l2ZW4gdHdvIGlucHV0cywgdGhlIGdlbmUgZXhwcmVzc2lvbiBtYXRyaXggX1lfIGFuZCB0aGUgcHJpb3Iga25vd2xlZGdlIChyZXByZXNlbnRlZCBhcyBiaW5hcnkgZ2VuZSBzZXQgbWVtYmVyc2hpcCBpbiBtYXRyaXggX0NfKSwgdGhlIG1ldGhvZCByZXR1cm5zIHRoZSBMVnMgKF9CXyksIHRoZWlyIGxvYWRpbmdzIChfWl8pLCBhbmQgYW4gYWRkaXRpb25hbCBzcGFyc2UgbWF0cml4IChfVV8pIHRoYXQgc3BlY2lmaWVzIHdoaWNoIChpZiBhbnkpIHByaW9yLWluZm9ybWF0aW9uIGdlbmUgc2V0cyBhbmQgcGF0aHdheXMgYXJlIHVzZWQgZm9yIGVhY2ggTFYuIFRoZSBsaWdodCBncmF5IGFyZWEgb2YgX1VfIGluZGljYXRlcyB0aGUgbGFyZ2UgbnVtYmVyIG9mIHplcm8gZWxlbWVudHMgb2YgdGhlIG1hdHJpeC4gV2UgYXBwbHkgb3VyIG1ldGhvZCB0byBhIHdob2xlLWJsb29kIGh1bWFuIGdlbmUgZXhwcmVzc2lvbiBkYXRhc2V0LiAqKmIqKiwgVGhlIHBvc2l0aXZlIGVudHJpZXMgb2YgdGhlIHJlc3VsdGluZyBfVV8gbWF0cml4IGFyZSB2aXN1YWxpemVkIGFzIGEgaGVhdCBtYXAsIGZhY2lsaXRhdGluZyB0aGUgaWRlbnRpZmljYXRpb24gb2YgdGhlIGNvcnJlc3BvbmRlbmNlIGJldHdlZW4gc3BlY2lmaWMgTFZzIGFuZCBwcmlvciBiaW9sb2dpY2FsIGtub3dsZWRnZS4gQXMgdGhlIGFic29sdXRlIHNjYWxlIG9mIHRoZSBfVV8gbWF0cml4IGlzIGFyYml0cmFyeSwgZWFjaCBjb2x1bW4gaXMgbm9ybWFsaXplZCB0byBhIG1heGltdW0gb2YgMS4gKipjKiosIFdlIHZhbGlkYXRlIHRoZSBMVnMgbWFwcGVkIHRvIHNwZWNpZmljIGxldWtvY3l0ZSBjZWxsIHR5cGVzIGJ5IGNvbXBhcmluZyBQTElFUiBlc3RpbWF0ZWQgcmVsYXRpdmUgY2VsbC10eXBlIHByb3BvcnRpb25zIHdpdGggZGlyZWN0IG1lYXN1cmVtZW50cyBieSBtYXNzIGN5dG9tZXRyeS4gRGFzaGVkIGxpbmVzIHJlcHJlc2VudCAwLjA1LCAwLjAxLCBhbmQgMC4wMDEgc2lnbmlmaWNhbmNlIGxldmVscyBmb3IgU3BlYXJtYW4gcmFuayBjb3JyZWxhdGlvbiAob25lLXRhaWxlZCB0ZXN0KS4gTksgY2VsbCwgbmF0dXJhbCBraWxsZXIgY2VsbC4KCkluIHRoZSBpbnRlcmVzdCBvZiB0aW1lLCB3ZSd2ZSBwcmVwYXJlZCB0aGUgbW9kZWwgaW4gYWR2YW5jZSB0aGF0IHdlIHdpbGwgcmVhZCBpbiBub3cuCllvdSBjYW4gc2VlIHRoZSBzdGVwcyB3ZSB0b29rIGluIGBzZXR1cC8wMi1wbGllci1wcmVwLlJtZGAuCgpgYGB7cn0KcGxpZXJfcmVzdWx0cyA8LSByZWFkX3JkcyhwbGllcl9maWxlKQpgYGAKClBMSUVSIGdpdmVzIHVzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIGEgbGF0ZW50IHZhcmlhYmxlIGl0IGxlYXJucyBhbmQgYW4gaW5wdXQgZ2VuZSBzZXQgaW4gdGhlIGBzdW1tYXJ5YCBkYXRhIGZyYW1lLgpJbiB0aGUgaW50ZXJlc3Qgb2YgdGltZSwgd2UncmUgZ29pbmcgdG8gZm9jdXMgaW4gb24gYSBzaW5nbGUgbGF0ZW50IHZhcmlhYmxlIHRvIGlsbHVzdHJhdGUgaG93IHlvdSBjb3VsZCB1c2UgdGhlc2UgdmFsdWVzLgoKYGBge3IgbHY1N19zdW1tYXJ5fQpwbGllcl9yZXN1bHRzJHN1bW1hcnkgJT4lIAogIGZpbHRlcihgTFYgaW5kZXhgID09IDU2LAogICAgICAgICBGRFIgPCAwLjA1KSAlPiUKICBhcnJhbmdlKEZEUikKYGBgCgpUaGUgX0JfIG1hdHJpeCBjb250YWlucyB0aGUgZXN0aW1hdGVzIG9yIGV4cHJlc3Npb24gdmFsdWVzIGZvciBhbGwgb2YgdGhlIGxhdGVudCB2YXJpYWJsZXMgbGVhcm5lZCBieSB0aGUgbW9kZWwuCgpgYGB7cn0KZGltKHBsaWVyX3Jlc3VsdHMkQikKYGBgCgpMZXQncyBnZXQgdGhpcyBpbnRvIGxvbmcgZm9ybWF0IGFuZCBhZGQgdGhlIHN1YnR5cGUgbGFiZWxzLgoKYGBge3J9Cmx2X2RmIDwtIGRhdGEuZnJhbWUocGxpZXJfcmVzdWx0cyRCKSAlPiUKICAjIERhdGEgZnJhbWUgd2hlcmUgdGhlIGxhdGVudCB2YXJpYWJsZSBuYW1lcyBhcmUgaW4gYSBjb2x1bW4KICByb3duYW1lc190b19jb2x1bW4odmFyID0gIkxWIikgJT4lCiAgIyBXaWRlIHRvIGxvbmcKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJCU18iKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJMVl9lc3RpbWF0ZSIpICU+JQogICMgQWRkIHRoZSBzdWJ0eXBlIGxhYmVscwogIGlubmVyX2pvaW4oc3VidHlwZV9kZiwgYnkgPSAiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIpCmBgYAoKQW5kIHBsb3QgdGhlIGVzdGltYXRlcyBmb3IgTFY1NiAtLSB3ZSBjYW4gdXNlIHRoaXMgdG8gZ2V0IGFuIGlkZWEgYWJvdXQgdGhlIGRpZmZlcmVuY2VzIGluIGltbXVuZS1yZWxhdGVkIHNpZ25hbHMgYmV0d2VlbiB0aGUgc3VidHlwZXMuCgpgYGB7cn0KbHZfZGYgJT4lCiAgIyBQTElFUiBuYW1lcyB0aGUgbGF0ZW50IHZhcmlhYmxlcyBiYXNlZCBvbiB0aGUgZ2VuZSBzZXRzIHRoYXQgdGhleSdyZQogICMgYXNzb2NpYXRlZCB3aXRoLCBidXQgYXMgd2Ugc2F3IGFib3ZlIHRoaXMgaXMgbm90IHRoZSBvbmx5IGdlbmUgc2V0IHRoYXQncwogICMgYXNzb2NpYXRlZCB3aXRoIHRoaXMgTFYhCiAgZmlsdGVyKExWID09ICI1NixTVk0gTWFjcm9waGFnZXMgTTIiKSAlPiUKICAjIE1ha2UgYSBib3hwbG90IHdoZXJlIHNhbXBsZXMgYXJlIGdyb3VwZWQgYnkgdGhlaXIgbW9sZWN1bGFyIHN1YnR5cGUgYW5kIAogICMgdGhhdCBzaG93cyB0aGUgaW5kaXZpZHVhbCBzYW1wbGVzIGFzIHBvaW50cwogIGdncGxvdChhZXMoeCA9IG1vbGVjdWxhcl9zdWJ0eXBlLCAKICAgICAgICAgICAgIHkgPSBMVl9lc3RpbWF0ZSwgCiAgICAgICAgICAgICBncm91cCA9IG1vbGVjdWxhcl9zdWJ0eXBlLCAKICAgICAgICAgICAgIGNvbG91ciA9IG1vbGVjdWxhcl9zdWJ0eXBlKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEpICsKICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMiwgYWxwaGEgPSAwLjUpICsKICAjIFVzZSB0aGUgc2FtZSB0aGVtZSBhbmQgY29sb3Igc2NoZW1lIHdlIGhhdmUgYmVlbiB1c2luZyB0aHJvdWdob3V0CiAgdGhlbWVfYncoKSArCiAgY29sb3JibGluZHI6OnNjYWxlX2NvbG9yX09rYWJlSXRvKCkKYGBgCgoqKlNvbWUgdGFrZWF3YXlzISoqCgotIERlcGVuZGluZyBvbiB0aGUgZ29hbHMgb2Ygb3VyIGFuYWx5c2lzLCB3ZSBtYXkgd2FudCB0byBzZWxlY3QgYSBkb21haW4tc3BlY2lmaWMgbWV0aG9kb2xvZ3kuCi0gV2UndmUgcmVkdWNlZCB0aGUgZGltZW5zaW9ucyBvZiBvdXIgZGF0YSBmcm9tIDEwcyBvZiAxMDAwcyBvZiBnZW5lcyB0byA1OCBsYXRlbnQgdmFyaWFibGVzIHRoYXQgd2UgY291bGQgdGVzdCBmb3IgZGlmZmVyZW5jZXMgYmV0d2VlbiBzdWJ0eXBlcy4gCgoKIyMgT3RoZXIgcmVzb3VyY2VzCgotIFRoaXMgd2Vic2l0ZSBleHBsYWlucyBbUENBIHZpc3VhbGx5XShodHRwOi8vc2V0b3NhLmlvL2V2L3ByaW5jaXBhbC1jb21wb25lbnQtYW5hbHlzaXMvKS4gIAotIFtXYXR0ZW5iZXJnICpldCBhbC4qICgyMDE2KV0oaHR0cHM6Ly9kaXN0aWxsLnB1Yi8yMDE2L21pc3JlYWQtdHNuZS8pIGRpc2N1c3NlcyBob3cgdG8gdXNlIHQtU05FIHByb3Blcmx5IHdpdGggZ3JlYXQgdmlzdWFscy4gCihUaGUgbGVzc29ucyBhcHBseSB0byBVTUFQIGFzIHdlbGwsIHdpdGggYSBicm9hZCBzdWJzdGl0dXRpb24gb2YgdGhlIGBuX25laWdoYm9yc2AgcGFyYW1ldGVyIGZvciBgcGVycGxleGl0eWAuKQotIFtOZ3V5ZW4gJiBIb2xtZXMgKDIwMTkpXShodHRwczovL2pvdXJuYWxzLnBsb3Mub3JnL3Bsb3Njb21wYmlvbC9hcnRpY2xlL2ZpbGU/aWQ9MTAuMTM3MS9qb3VybmFsLnBjYmkuMTAwNjkwNyZ0eXBlPXByaW50YWJsZSkgbGF5IG91dCBndWlkZWxpbmVzIG9uIGNob29zaW5nIGRpbWVuc2lvbnMgcmVkdWN0aW9uIG1ldGhvZHMuICAKLSBbRnJleXRhZyAoMjAxOSldKGh0dHBzOi8vcnB1YnMuY29tL1Nhc2tpYS81MjAyMTYpIGlzIGEgbmljZSBleHBsYW5hdGlvbiBhbmQgY29tcGFyaXNvbiBvZiBtYW55IGRpZmZlcmVudCBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gdGVjaG5pcXVlcyB0aGF0IHlvdSBtYXkgZW5jb3VudGVyLgotIFtSYWogKDIwMTkpXShodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vZGltZW5zaW9uYWxpdHktcmVkdWN0aW9uLWZvci1tYWNoaW5lLWxlYXJuaW5nLTgwYTQ2YzJlYmI3ZSkgLSBhbm90aGVyIG5pY2UgaW50cm9kdWN0aW9uIHRvIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbgoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAK